Sensor Fusion for Kinetis MCUs (ISSDK/KSDK version)
magnetic.h File Reference
+ This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  MagBuffer
 
struct  MagCalibration
 

Macros

#define F_USING_MAG   0x0002
 
#define DEFAULTB   50.0F
 
Magnetic Calibration Constants
#define MAGBUFFSIZEX   14
 
#define MAGBUFFSIZEY   (2 * MAGBUFFSIZEX)
 
#define MINMEASUREMENTS4CAL   110
 
#define MINMEASUREMENTS7CAL   220
 
#define MINMEASUREMENTS10CAL   330
 
#define MAXMEASUREMENTS   360
 
#define CAL_INTERVAL_SECS   300
 
#define MINBFITUT   10.0F
 
#define MAXBFITUT   90.0F
 
#define FITERRORAGINGSECS   86400.0F
 
#define MESHDELTACOUNTS   50
 

Typedefs

typedef struct MagBuffer MagBuffer
 
typedef struct MagCalibration MagCalibration
 

Functions

Function prototypes for functions in magnetic.c

These functions comprise the core of the magnetic calibration features of the library. Parameter descriptions are not included here, as details are provided in sensor_fusion.h.

void fInitializeMagCalibration (struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer)
 
void iUpdateMagBuffer (struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag, int32_t loopcounter)
 
void fInvertMagCal (struct MagSensor *pthisMag, struct MagCalibration *pthisMagCal)
 
void fRunMagCalibration (struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag, int32_t loopcounter)
 
void fUpdateMagCalibration4 (struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
 
void fUpdateMagCalibration7 (struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
 
void fUpdateMagCalibration10 (struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
 
void fUpdateMagCalibration4Slice (struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
 
void fUpdateMagCalibration7Slice (struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
 
void fUpdateMagCalibration10Slice (struct MagCalibration *pthisMagCal, struct MagBuffer *pthisMagBuffer, struct MagSensor *pthisMag)
 

Detailed Description

Many developers can utilize the NXP Sensor Fusion Library without ever making any adjustment to the lower level magnetic calibration functions defined in this file.

Definition in file magnetic.h.

Macro Definition Documentation

#define CAL_INTERVAL_SECS   300

300s or 5min interval for regular calibration checks

Definition at line 50 of file magnetic.h.

Referenced by fRunMagCalibration().

#define FITERRORAGINGSECS   86400.0F

24 hours: time (s) for fit error to increase (age) by e=2.718

Definition at line 53 of file magnetic.h.

Referenced by fRunMagCalibration().

#define MAXBFITUT   90.0F

maximum acceptable geomagnetic field B (uT) for valid calibration

Definition at line 52 of file magnetic.h.

Referenced by fRunMagCalibration().

#define MAXMEASUREMENTS   360

maximum number of measurements used for calibration

Definition at line 49 of file magnetic.h.

Referenced by iUpdateMagBuffer().

#define MESHDELTACOUNTS   50

magnetic buffer mesh spacing in counts (here 5uT)

Definition at line 54 of file magnetic.h.

Referenced by iUpdateMagBuffer().

#define MINBFITUT   10.0F

minimum acceptable geomagnetic field B (uT) for valid calibration

Definition at line 51 of file magnetic.h.

Referenced by fRunMagCalibration().

#define MINMEASUREMENTS10CAL   330

minimum number of measurements for 10 element calibration

Definition at line 48 of file magnetic.h.

Referenced by fRunMagCalibration().

#define MINMEASUREMENTS4CAL   110

minimum number of measurements for 4 element calibration

Definition at line 46 of file magnetic.h.

Referenced by fRunMagCalibration().

#define MINMEASUREMENTS7CAL   220

minimum number of measurements for 7 element calibration

Definition at line 47 of file magnetic.h.

Referenced by fRunMagCalibration().

Typedef Documentation

typedef struct MagBuffer MagBuffer

The Magnetometer Measurement Buffer holds a 3-dimensional "constellation" of data points.

The constellation of points are used to compute magnetic hard/soft iron compensation terms. The contents of this buffer are updated on a continuing basis.

Magnetic Calibration Structure.

Function Documentation

void fInitializeMagCalibration ( struct MagCalibration pthisMagCal,
struct MagBuffer pthisMagBuffer 
)

Definition at line 41 of file magnetic.c.

Referenced by DecodeCommandBytes(), and initializeFusionEngine().

43 {
44  float *pFlash; // pointer into flash memory
45  int8 i,
46  j; // loop counters
47 
48  // set magnetic buffer index to invalid value -1 to denote no measurement present
49  pthisMagBuffer->iMagBufferCount = 0;
50  for (i = 0; i < MAGBUFFSIZEX; i++)
51  for (j = 0; j < MAGBUFFSIZEY; j++) pthisMagBuffer->index[i][j] = -1;
52 
53  // initialize the array of (MAGBUFFSIZEX - 1) elements of 100 * tangents used for buffer indexing
54  // entries cover the range 100 * tan(-PI/2 + PI/MAGBUFFSIZEX), 100 * tan(-PI/2 + 2*PI/MAGBUFFSIZEX) to
55  // 100 * tan(-PI/2 + (MAGBUFFSIZEX - 1) * PI/MAGBUFFSIZEX).
56  // for MAGBUFFSIZEX=12, the entries range in value from -373 to +373
57  for (i = 0; i < (MAGBUFFSIZEX - 1); i++)
58  pthisMagBuffer->tanarray[i] = (int16) (100.0F * tanf(PI * (-0.5F + (float) (i + 1) / MAGBUFFSIZEX)));
59 
60  // check to see if the stored magnetic calibration has been erased
61  // the standard value for erased flash is 0xFF in each byte but for portability check against 0x12345678
62  pFlash = (float *) (CALIBRATION_NVM_ADDR + MAG_NVM_OFFSET);
63  if (*((uint32 *) pFlash++) == 0x12345678)
64  {
65  // a magnetic calibration is present in flash
66  // copy magnetic calibration elements (15x float + 1x int32 total 64 bytes) from flash to RAM
67  for (i = CHX; i <= CHZ; i++) pthisMagCal->fV[i] = *(pFlash++);
68  for (i = CHX; i <= CHZ; i++)
69  for (j = CHX; j <= CHZ; j++)
70  pthisMagCal->finvW[i][j] = *(pFlash++);
71  pthisMagCal->fB = *(pFlash++);
72  pthisMagCal->fBSq = *(pFlash++);
73  pthisMagCal->fFitErrorpc = *(pFlash++);
74  pthisMagCal->iValidMagCal = *((int32 *) pFlash);
75  }
76  else
77  {
78  // flash has been erased and no magnetic calibration is present
79  // initialize the magnetic calibration in RAM to null default
80  pthisMagCal->fV[CHX] = pthisMagCal->fV[CHY] = pthisMagCal->fV[CHZ] = 0.0F;
81  f3x3matrixAeqI(pthisMagCal->finvW);
82  pthisMagCal->fB = DEFAULTB;
83  pthisMagCal->fBSq = DEFAULTB * DEFAULTB;
84  pthisMagCal->fFitErrorpc = 100.0F;
85  pthisMagCal->iValidMagCal = 0;
86  }
87 
88  // initialize remaining elements of the magnetic calibration structure
89  pthisMagCal->iCalInProgress = 0;
90  pthisMagCal->iInitiateMagCal = 0;
91  pthisMagCal->iNewCalibrationAvailable = 0;
92  pthisMagCal->iMagBufferReadOnly = false;
93  pthisMagCal->i4ElementSolverTried = false;
94  pthisMagCal->i7ElementSolverTried = false;
95  pthisMagCal->i10ElementSolverTried = false;
96 
97  return;
98 }
int8_t i7ElementSolverTried
flag to denote at least one attempt made with 4 element calibration
Definition: magnetic.h:102
#define MAGBUFFSIZEX
x dimension in magnetometer buffer (12x24 equals 288 elements)
Definition: magnetic.h:44
#define CHY
Used to access Y-channel entries in various data data structures.
Definition: sensor_fusion.h:77
#define DEFAULTB
int16_t tanarray[MAGBUFFSIZEX-1]
array of tangents of (100 * angle)
Definition: magnetic.h:67
int8_t iNewCalibrationAvailable
flag denoting that a new calibration has been computed
Definition: magnetic.h:98
int8_t iInitiateMagCal
flag to start a new magnetic calibration
Definition: magnetic.h:99
int16_t iMagBufferCount
number of magnetometer readings
Definition: magnetic.h:68
#define PI
pi
Definition: sensor_fusion.h:97
int32_t int32
Definition: sensor_fusion.h:57
uint32_t uint32
Definition: sensor_fusion.h:60
int8_t i4ElementSolverTried
flag to denote at least one attempt made with 4 element calibration
Definition: magnetic.h:101
int32_t iValidMagCal
solver used: 0 (no calibration) or 4, 7, 10 element
Definition: magnetic.h:80
float finvW[3][3]
current inverse soft iron matrix
Definition: magnetic.h:76
int8_t iMagBufferReadOnly
flag to denote that the magnetic measurement buffer is temporarily read only
Definition: magnetic.h:100
float fV[3]
current hard iron offset x, y, z, (uT)
Definition: magnetic.h:75
#define CHZ
#define CHX
Used to access X-channel entries in various data data structures.
Definition: sensor_fusion.h:76
float fFitErrorpc
current fit error %
Definition: magnetic.h:79
void f3x3matrixAeqI(float A[][3])
function sets the 3x3 matrix A to the identity matrix
Definition: matrix.c:45
int8_t iCalInProgress
flag denoting that a calibration is in progress
Definition: magnetic.h:97
int32_t index[MAGBUFFSIZEX][MAGBUFFSIZEY]
array of time indices
Definition: magnetic.h:66
float fB
current geomagnetic field magnitude (uT)
Definition: magnetic.h:77
int16_t int16
Definition: sensor_fusion.h:56
float fBSq
square of fB (uT^2)
Definition: magnetic.h:78
#define MAGBUFFSIZEY
y dimension in magnetometer buffer (12x24 equals 288 elements)
Definition: magnetic.h:45
int8_t int8
Definition: sensor_fusion.h:55
int8_t i10ElementSolverTried
flag to denote at least one attempt made with 4 element calibration
Definition: magnetic.h:103

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void fInvertMagCal ( struct MagSensor pthisMag,
struct MagCalibration pthisMagCal 
)

Definition at line 297 of file magnetic.c.

Referenced by processMagData().

298 {
299  // local variables
300  float ftmp[3]; // temporary array
301  int8 i; // loop counter
302 
303  // remove the computed hard iron offsets (uT): ftmp[]=fBs[]-V[]
304  for (i = CHX; i <= CHZ; i++)
305  {
306  ftmp[i] = pthisMag->fBs[i] - pthisMagCal->fV[i];
307  }
308 
309  // remove the computed soft iron offsets (uT and counts): fBc=inv(W)*(fBs[]-V[])
310  for (i = CHX; i <= CHZ; i++)
311  {
312  pthisMag->fBc[i] = pthisMagCal->finvW[i][CHX] *
313  ftmp[CHX] +
314  pthisMagCal->finvW[i][CHY] *
315  ftmp[CHY] +
316  pthisMagCal->finvW[i][CHZ] *
317  ftmp[CHZ];
318  pthisMag->iBc[i] = (int16) (pthisMag->fBc[i] * pthisMag->fCountsPeruT);
319  }
320 
321  return;
322 }
#define CHY
Used to access Y-channel entries in various data data structures.
Definition: sensor_fusion.h:77
float fBc[3]
averaged calibrated measurement (uT)
float finvW[3][3]
current inverse soft iron matrix
Definition: magnetic.h:76
float fV[3]
current hard iron offset x, y, z, (uT)
Definition: magnetic.h:75
#define CHZ
int16_t iBc[3]
averaged calibrated measurement (counts)
#define CHX
Used to access X-channel entries in various data data structures.
Definition: sensor_fusion.h:76
float fCountsPeruT
counts per uT
float fBs[3]
averaged un-calibrated measurement (uT)
int16_t int16
Definition: sensor_fusion.h:56
int8_t int8
Definition: sensor_fusion.h:55

+ Here is the caller graph for this function:

void fRunMagCalibration ( struct MagCalibration pthisMagCal,
struct MagBuffer pthisMagBuffer,
struct MagSensor pthisMag,
int32_t  loopcounter 
)

Definition at line 325 of file magnetic.c.

Referenced by processMagData().

327 {
328  int8 i,
329  j; // loop counters
330 
331  // determine whether to initiate a new magnetic calibration
332  if (!pthisMagCal->iCalInProgress)
333  {
334  // clear the flag
335  pthisMagCal->iInitiateMagCal = 0;
336 
337  // try one calibration attempt with the best model available given the number of measurements
338  if ((pthisMagBuffer->iMagBufferCount >= MINMEASUREMENTS10CAL) &&
339  (!pthisMagCal->i10ElementSolverTried))
340  {
341  pthisMagCal->i10ElementSolverTried = true;
342  pthisMagCal->iInitiateMagCal = 10;
343  }
344  else if ((pthisMagBuffer->iMagBufferCount >= MINMEASUREMENTS7CAL) &&
345  (!pthisMagCal->i7ElementSolverTried))
346  {
347  pthisMagCal->i7ElementSolverTried = true;
348  pthisMagCal->iInitiateMagCal = 7;
349  }
350  else if ((pthisMagBuffer->iMagBufferCount >= MINMEASUREMENTS4CAL) &&
351  (!pthisMagCal->i4ElementSolverTried))
352  {
353  pthisMagCal->i4ElementSolverTried = true;
354  pthisMagCal->iInitiateMagCal = 4;
355  }
356 
357  // otherwise start a calibration at regular interval defined by CAL_INTERVAL_SECS
358  else if (!pthisMagCal->iInitiateMagCal &&
359  !(loopcounter % (CAL_INTERVAL_SECS * FUSION_HZ)))
360  {
361  if (pthisMagBuffer->iMagBufferCount >= MINMEASUREMENTS10CAL)
362  {
363  pthisMagCal->i10ElementSolverTried = true;
364  pthisMagCal->iInitiateMagCal = 10;
365  }
366  else if (pthisMagBuffer->iMagBufferCount >= MINMEASUREMENTS7CAL)
367  {
368  pthisMagCal->i7ElementSolverTried = true;
369  pthisMagCal->iInitiateMagCal = 7;
370  }
371  else if (pthisMagBuffer->iMagBufferCount >= MINMEASUREMENTS4CAL)
372  {
373  pthisMagCal->i4ElementSolverTried = true;
374  pthisMagCal->iInitiateMagCal = 4;
375  }
376  }
377 
378  // store the selected calibration model (if any) to be run
379  pthisMagCal->iCalInProgress = pthisMagCal->iInitiateMagCal;
380  }
381 
382  // on entry each of the calibration functions resets iInitiateMagCal and on completion sets
383  // iCalInProgress=0 and iNewCalibrationAvailable=4,7,10 according to the solver used
384  switch (pthisMagCal->iCalInProgress)
385  {
386  case 0:
387  break;
388 
389  case 4:
390  fUpdateMagCalibration4Slice(pthisMagCal, pthisMagBuffer, pthisMag);
391  break;
392 
393  case 7:
394  fUpdateMagCalibration7Slice(pthisMagCal, pthisMagBuffer, pthisMag);
395  break;
396 
397  case 10:
398  fUpdateMagCalibration10Slice(pthisMagCal, pthisMagBuffer, pthisMag);
399  break;
400 
401  default:
402  break;
403  }
404 
405  // evaluate the new calibration to determine whether to accept it
406  if (pthisMagCal->iNewCalibrationAvailable)
407  {
408  // the geomagnetic field strength must be in range (earth is 22uT to 67uT) with reasonable fit error
409  if ((pthisMagCal->ftrB >= MINBFITUT) && (pthisMagCal->ftrB <= MAXBFITUT) &&
410  (pthisMagCal->ftrFitErrorpc <= 15.0F))
411  {
412  // the fit error must be improved or be from a more sophisticated solver but still 5 bars (under 3.5% fit error)
413  if ((pthisMagCal->ftrFitErrorpc <= pthisMagCal->fFitErrorpc) || ((
414  pthisMagCal->iNewCalibrationAvailable > pthisMagCal->
415  iValidMagCal) && (pthisMagCal->ftrFitErrorpc <= 3.5F)))
416  {
417  // accept the new calibration
418  pthisMagCal->iValidMagCal = pthisMagCal->iNewCalibrationAvailable;
419  pthisMagCal->fFitErrorpc = pthisMagCal->ftrFitErrorpc;
420  pthisMagCal->fB = pthisMagCal->ftrB;
421  pthisMagCal->fBSq = pthisMagCal->fB * pthisMagCal->fB;
422  for (i = CHX; i <= CHZ; i++)
423  {
424  pthisMagCal->fV[i] = pthisMagCal->ftrV[i];
425  for (j = CHX; j <= CHZ; j++)
426  pthisMagCal->finvW[i][j] = pthisMagCal->ftrinvW[i][j];
427  }
428  }
429  }
430  else
431  {
432  // the magnetic buffer is presumed corrupted so clear out all measurements and restart calibration attempts
433  pthisMagBuffer->iMagBufferCount = 0;
434  for (i = 0; i < MAGBUFFSIZEX; i++)
435  for (j = 0; j < MAGBUFFSIZEY; j++)
436  pthisMagBuffer->index[i][j] = -1;
437  pthisMagCal->i4ElementSolverTried = false;
438  pthisMagCal->i7ElementSolverTried = false;
439  pthisMagCal->i10ElementSolverTried = false;
440  } // end of test for new calibration within field strength and fit error limits
441 
442  // reset the new calibration flag
443  pthisMagCal->iNewCalibrationAvailable = 0;
444  } // end of test for new calibration available
445 
446  // age the existing fit error very slowly to avoid one good calibration locking out future updates.
447  // this prevents a calibration remaining for ever if a unit is never powered down
448  if (pthisMagCal->iValidMagCal)
449  pthisMagCal->fFitErrorpc += 1.0F / ((float) FUSION_HZ * FITERRORAGINGSECS);
450 
451  return;
452 }
float ftrB
trial value of geomagnetic field magnitude in uT
Definition: magnetic.h:85
int8_t i7ElementSolverTried
flag to denote at least one attempt made with 4 element calibration
Definition: magnetic.h:102
#define MAGBUFFSIZEX
x dimension in magnetometer buffer (12x24 equals 288 elements)
Definition: magnetic.h:44
#define MINMEASUREMENTS7CAL
minimum number of measurements for 7 element calibration
Definition: magnetic.h:47
#define FITERRORAGINGSECS
24 hours: time (s) for fit error to increase (age) by e=2.718
Definition: magnetic.h:53
int8_t iNewCalibrationAvailable
flag denoting that a new calibration has been computed
Definition: magnetic.h:98
int8_t iInitiateMagCal
flag to start a new magnetic calibration
Definition: magnetic.h:99
int16_t iMagBufferCount
number of magnetometer readings
Definition: magnetic.h:68
float ftrV[3]
trial value of hard iron offset z, y, z (uT)
Definition: magnetic.h:83
float ftrinvW[3][3]
trial inverse soft iron matrix size
Definition: magnetic.h:84
int8_t i4ElementSolverTried
flag to denote at least one attempt made with 4 element calibration
Definition: magnetic.h:101
int32_t iValidMagCal
solver used: 0 (no calibration) or 4, 7, 10 element
Definition: magnetic.h:80
float ftrFitErrorpc
trial value of fit error %
Definition: magnetic.h:86
void fUpdateMagCalibration7Slice(MagCalibration *pthisMagCal, MagBuffer *pthisMagBuffer, MagSensor *pthisMag)
Definition: magnetic.c:686
float finvW[3][3]
current inverse soft iron matrix
Definition: magnetic.h:76
float fV[3]
current hard iron offset x, y, z, (uT)
Definition: magnetic.h:75
#define MINMEASUREMENTS10CAL
minimum number of measurements for 10 element calibration
Definition: magnetic.h:48
#define CAL_INTERVAL_SECS
300s or 5min interval for regular calibration checks
Definition: magnetic.h:50
#define CHZ
void fUpdateMagCalibration10Slice(MagCalibration *pthisMagCal, MagBuffer *pthisMagBuffer, MagSensor *pthisMag)
Definition: magnetic.c:973
void fUpdateMagCalibration4Slice(MagCalibration *pthisMagCal, MagBuffer *pthisMagBuffer, MagSensor *pthisMag)
Definition: magnetic.c:455
#define CHX
Used to access X-channel entries in various data data structures.
Definition: sensor_fusion.h:76
float fFitErrorpc
current fit error %
Definition: magnetic.h:79
int8_t iCalInProgress
flag denoting that a calibration is in progress
Definition: magnetic.h:97
#define FUSION_HZ
(int) actual rate of fusion algorithm execution and sensor FIFO reads
int32_t index[MAGBUFFSIZEX][MAGBUFFSIZEY]
array of time indices
Definition: magnetic.h:66
#define MAXBFITUT
maximum acceptable geomagnetic field B (uT) for valid calibration
Definition: magnetic.h:52
#define MINMEASUREMENTS4CAL
minimum number of measurements for 4 element calibration
Definition: magnetic.h:46
float fB
current geomagnetic field magnitude (uT)
Definition: magnetic.h:77
float fBSq
square of fB (uT^2)
Definition: magnetic.h:78
#define MAGBUFFSIZEY
y dimension in magnetometer buffer (12x24 equals 288 elements)
Definition: magnetic.h:45
int8_t int8
Definition: sensor_fusion.h:55
#define MINBFITUT
minimum acceptable geomagnetic field B (uT) for valid calibration
Definition: magnetic.h:51
int8_t i10ElementSolverTried
flag to denote at least one attempt made with 4 element calibration
Definition: magnetic.h:103

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void fUpdateMagCalibration10 ( struct MagCalibration pthisMagCal,
struct MagBuffer pthisMagBuffer,
struct MagSensor pthisMag 
)
void fUpdateMagCalibration10Slice ( struct MagCalibration pthisMagCal,
struct MagBuffer pthisMagBuffer,
struct MagSensor pthisMag 
)

Definition at line 973 of file magnetic.c.

Referenced by fRunMagCalibration().

975 {
976  // local variables
977  float fresidue; // eigen-decomposition residual sum
978  float ftmp; // scratch variable
979  int8 i,
980  j,
981  k,
982  l; // loop counters
983 #define MATRIX_10_SIZE 10
984  // reset the time slice to zero if iInitiateMagCal is set and then clear iInitiateMagCal
985  if (pthisMagCal->iInitiateMagCal)
986  {
987  pthisMagCal->itimeslice = 0;
988  pthisMagCal->iInitiateMagCal = false;
989  pthisMagCal->iMagBufferReadOnly = true;
990  }
991 
992  // time slice 0: 18.7k KL25Z ticks for 300 measurements = 0.39ms on KL25Z (variable) stored in systick[0]
993  // zero measurement matrix fmatA and calculate the mean values in the magnetic buffer
994  if (pthisMagCal->itimeslice == 0)
995  {
996  int16 iM; // number of measurements in the magnetic buffer
997 
998  // zero the on and above diagonal elements of the 10x10 symmetric measurement matrix fmatA
999  for (i = 0; i < MATRIX_10_SIZE; i++)
1000  for (j = i; j < MATRIX_10_SIZE; j++)
1001  pthisMagCal->fmatA[i][j] = 0.0F;
1002 
1003  // compute the sum of measurements in the magnetic buffer
1004  iM = 0;
1005  for (i = 0; i < 3; i++) pthisMagCal->iSumBs[i] = 0;
1006  for (i = 0; i < MAGBUFFSIZEX; i++)
1007  {
1008  for (j = 0; j < MAGBUFFSIZEY; j++)
1009  {
1010  if (pthisMagBuffer->index[i][j] != -1)
1011  {
1012  iM++;
1013  for (k = 0; k < 3; k++)
1014  pthisMagCal->iSumBs[k] += (int32) pthisMagBuffer->iBs[k][i][j];
1015  }
1016  }
1017  }
1018 
1019  // compute the magnetic buffer measurement averages with nearest integer rounding
1020  for (i = 0; i < 3; i++)
1021  {
1022  if (pthisMagCal->iSumBs[i] >= 0)
1023  pthisMagCal->iMeanBs[i] =
1024  (
1025  pthisMagCal->iSumBs[i] +
1026  ((int32) iM >> 1)
1027  ) /
1028  (int32) iM;
1029  else
1030  pthisMagCal->iMeanBs[i] =
1031  (
1032  pthisMagCal->iSumBs[i] -
1033  ((int32) iM >> 1)
1034  ) /
1035  (int32) iM;
1036  }
1037 
1038  // as defensive programming also ensure the number of measurements found is re-stored
1039  pthisMagBuffer->iMagBufferCount = iM;
1040 
1041  // increment the time slice for the next iteration
1042  (pthisMagCal->itimeslice)++;
1043  } // end of time slice 0
1044 
1045  // time slices 1 to MAGBUFFSIZEX * MAGBUFFSIZEY: accumulate matrices: 17.9k KL25Z ticks = 0.37ms for 300 measurements
1046  // (with max measured stored in systick[1])
1047  else if ((pthisMagCal->itimeslice >= 1) &&
1048  (pthisMagCal->itimeslice <= MAGBUFFSIZEX * MAGBUFFSIZEY))
1049  {
1050  // accumulate the symmetric matrix fmatA on the zero mean measurements
1051  i = (pthisMagCal->itimeslice - 1) / MAGBUFFSIZEY; // matrix row i ranges 0 to MAGBUFFSIZEX-1
1052  j = (pthisMagCal->itimeslice - 1) % MAGBUFFSIZEY; // matrix column j ranges 0 to MAGBUFFSIZEY-1
1053  if (pthisMagBuffer->index[i][j] != -1)
1054  {
1055  // set fvecA[6-8] to the zero mean measurements
1056  for (k = 0; k < 3; k++)
1057  pthisMagCal->fvecA[k + 6] = (float)
1058  (
1059  (int32) pthisMagBuffer->iBs[k][i][j] -
1060  (int32) pthisMagCal->iMeanBs[k]
1061  );
1062 
1063  // compute fvecA[0-5] from fvecA[6-8]
1064  pthisMagCal->fvecA[0] = pthisMagCal->fvecA[6] * pthisMagCal->fvecA[6];
1065  pthisMagCal->fvecA[1] = 2.0F *
1066  pthisMagCal->fvecA[6] *
1067  pthisMagCal->fvecA[7];
1068  pthisMagCal->fvecA[2] = 2.0F *
1069  pthisMagCal->fvecA[6] *
1070  pthisMagCal->fvecA[8];
1071  pthisMagCal->fvecA[3] = pthisMagCal->fvecA[7] * pthisMagCal->fvecA[7];
1072  pthisMagCal->fvecA[4] = 2.0F *
1073  pthisMagCal->fvecA[7] *
1074  pthisMagCal->fvecA[8];
1075  pthisMagCal->fvecA[5] = pthisMagCal->fvecA[8] * pthisMagCal->fvecA[8];
1076 
1077  // update non-zero elements fmatA[0-5][9] of fmatA ignoring fmatA[9][9] which is set later.
1078  // elements fmatA[6-8][9] are zero as a result of subtracting the mean value
1079  for (k = 0; k < 6; k++)
1080  pthisMagCal->fmatA[k][9] += pthisMagCal->fvecA[k];
1081 
1082  // update the remaining on and above diagonal elements fmatA[0-8][0-8]
1083  for (k = 0; k < (MATRIX_10_SIZE - 1); k++)
1084  {
1085  for (l = k; l < (MATRIX_10_SIZE - 1); l++)
1086  pthisMagCal->fmatA[k][l] += pthisMagCal->fvecA[k] * pthisMagCal->fvecA[l];
1087  }
1088  }
1089 
1090  // increment the time slice for the next iteration
1091  (pthisMagCal->itimeslice)++;
1092  } // end of time slices 1 to MAGBUFFSIZEX * MAGBUFFSIZEY
1093 
1094  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 1: 1.2k ticks on KL25Z = 0.025ms on KL25Z (constant) (stored in systick[2])
1095  // re-enable magnetic buffer for writing and prepare fmatA, fmatB, fvecA for eigendecomposition
1096  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 1))
1097  {
1098  // set fmatA[9][9] to the number of magnetic measurements found
1099  pthisMagCal->fmatA[MATRIX_10_SIZE - 1][MATRIX_10_SIZE - 1] = (float) pthisMagBuffer->iMagBufferCount;
1100 
1101  // clear the magnetic buffer read only flag now that the matrices have been computed
1102  pthisMagCal->iMagBufferReadOnly = false;
1103 
1104  // set below diagonal elements of 10x10 matrix fmatA to above diagonal elements
1105  for (i = 1; i < MATRIX_10_SIZE; i++)
1106  for (j = 0; j < i; j++)
1107  pthisMagCal->fmatA[i][j] = pthisMagCal->fmatA[j][i];
1108 
1109  // set matrix of eigenvectors fmatB to identity matrix and eigenvalues vector fvecA to diagonal elements of fmatA
1110  for (i = 0; i < MATRIX_10_SIZE; i++)
1111  {
1112  for (j = 0; j < MATRIX_10_SIZE; j++)
1113  pthisMagCal->fmatB[i][j] = 0.0F;
1114  pthisMagCal->fmatB[i][i] = 1.0F;
1115  pthisMagCal->fvecA[i] = pthisMagCal->fmatA[i][i];
1116  }
1117 
1118  // increment the time slice for the next iteration
1119  (pthisMagCal->itimeslice)++;
1120  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 1
1121 
1122  // repeating 45 time slices MAGBUFFSIZEX * MAGBUFFSIZEY + 2 to MAGBUFFSIZEX * MAGBUFFSIZEY + 46 inclusive
1123  // to perform the eigendecomposition of the measurement matrix fmatA.
1124  // 28.2k ticks = 0.56ms on KL25Z (with max stored in systick[3]).
1125  // for a 10x10 matrix there are 45 above diagonal elements: 9+8+7+6+5+4+3+2+1+0=45
1126  else if ((pthisMagCal->itimeslice >= (MAGBUFFSIZEX * MAGBUFFSIZEY + 2)) &&
1127  (pthisMagCal->itimeslice <= (MAGBUFFSIZEX * MAGBUFFSIZEY + 46)))
1128  {
1129  // set k to the matrix element of interest in range 0 to 44 to be zeroed and set row i and column j
1130  k = pthisMagCal->itimeslice - (MAGBUFFSIZEX * MAGBUFFSIZEY + 2);
1131  if (k < 9)
1132  {
1133  i = 0;
1134  j = k + 1;
1135  }
1136  else if (k < 17)
1137  {
1138  i = 1;
1139  j = k - 7;
1140  }
1141  else if (k < 24)
1142  {
1143  i = 2;
1144  j = k - 14;
1145  }
1146  else if (k < 30)
1147  {
1148  i = 3;
1149  j = k - 20;
1150  }
1151  else if (k < 35)
1152  {
1153  i = 4;
1154  j = k - 25;
1155  }
1156  else if (k < 39)
1157  {
1158  i = 5;
1159  j = k - 29;
1160  }
1161  else if (k < 42)
1162  {
1163  i = 6;
1164  j = k - 32;
1165  }
1166  else if (k < 44)
1167  {
1168  i = 7;
1169  j = k - 34;
1170  }
1171  else
1172  {
1173  i = 8;
1174  j = 9;
1175  }
1176 
1177  // only continue if matrix element i, j has not already been zeroed
1178  if (fabsf(pthisMagCal->fmatA[i][j]) > 0.0F)
1179  fComputeEigSlice(pthisMagCal->fmatA, pthisMagCal->fmatB,
1180  pthisMagCal->fvecA, i, j, MATRIX_10_SIZE);
1181 
1182  // increment the time slice for the next iteration
1183  (pthisMagCal->itimeslice)++;
1184  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 2 to MAGBUFFSIZEX * MAGBUFFSIZEY + 46 inclusive
1185 
1186  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 47: 5.6k ticks on KL25Z = 0.12ms on KL25Z (constant) (stored in systick[4])
1187  // compute the sum of the absolute values of above diagonal elements in fmatA as eigen-decomposition exit criterion
1188  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 47))
1189  {
1190  // sum residue of all above-diagonal elements
1191  fresidue = 0.0F;
1192  for (i = 0; i < MATRIX_10_SIZE; i++)
1193  for (j = i + 1; j < MATRIX_10_SIZE; j++)
1194  fresidue += fabsf(pthisMagCal->fmatA[i][j]);
1195 
1196  // determine whether to re-enter the eigen-decomposition or skip to calculation of the calibration coefficients
1197  if (fresidue > 0.0F)
1198  // continue the eigen-decomposition
1199  (pthisMagCal->itimeslice) = MAGBUFFSIZEX * MAGBUFFSIZEY + 2;
1200  else
1201  // continue to compute the calibration coefficients since the eigen-decomposition is complete
1202  (pthisMagCal->itimeslice)++;
1203  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 47
1204 
1205  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 48: 38.5k ticks = 0.80ms on KL25Z (constant) (stored in systick[5])
1206  // compute the calibration coefficients (excluding invW) from the solution eigenvector
1207  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 48))
1208  {
1209  float fdetA; // determinant of ellipsoid matrix A
1210  int8 imin; // column of solution eigenvector with minimum eigenvalue
1211 
1212  // set imin to the index of the smallest eigenvalue in fvecA
1213  imin = 0;
1214  for (i = 1; i < MATRIX_10_SIZE; i++)
1215  if (pthisMagCal->fvecA[i] < pthisMagCal->fvecA[imin]) imin = i;
1216 
1217  // set the ellipsoid matrix A from elements 0 to 5 of the solution eigenvector.
1218  pthisMagCal->fA[CHX][CHX] = pthisMagCal->fmatB[0][imin];
1219  pthisMagCal->fA[CHX][CHY] = pthisMagCal->fA[CHY][CHX] = pthisMagCal->fmatB[1][imin];
1220  pthisMagCal->fA[CHX][CHZ] = pthisMagCal->fA[CHZ][CHX] = pthisMagCal->fmatB[2][imin];
1221  pthisMagCal->fA[CHY][CHY] = pthisMagCal->fmatB[3][imin];
1222  pthisMagCal->fA[CHY][CHZ] = pthisMagCal->fA[CHZ][CHY] = pthisMagCal->fmatB[4][imin];
1223  pthisMagCal->fA[CHZ][CHZ] = pthisMagCal->fmatB[5][imin];
1224 
1225  // negate A and the entire solution vector A has negative determinant
1226  fdetA = f3x3matrixDetA(pthisMagCal->fA);
1227  if (fdetA < 0.0F)
1228  {
1229  f3x3matrixAeqMinusA(pthisMagCal->fA);
1230  fdetA = fabs(fdetA);
1231  for (i = 0; i < MATRIX_10_SIZE; i++)
1232  pthisMagCal->fmatB[i][imin] = -pthisMagCal->fmatB[i][imin];
1233  }
1234 
1235  // set finvA to the inverse of the ellipsoid matrix fA
1236  f3x3matrixAeqInvSymB(pthisMagCal->finvA, pthisMagCal->fA);
1237 
1238  // compute the hard iron offset fV for zero mean data (counts)
1239  for (i = CHX; i <= CHZ; i++)
1240  {
1241  pthisMagCal->ftrV[i] = pthisMagCal->finvA[i][0] *
1242  pthisMagCal->fmatB[6][imin] +
1243  pthisMagCal->finvA[i][1] *
1244  pthisMagCal->fmatB[7][imin] +
1245  pthisMagCal->finvA[i][2] *
1246  pthisMagCal->fmatB[8][imin];
1247  pthisMagCal->ftrV[i] *= -0.5F;
1248  }
1249 
1250  // compute the geomagnetic field strength B (counts) for current (un-normalized) ellipsoid matrix determinant.
1251  ftmp = pthisMagCal->fA[CHX][CHY] *
1252  pthisMagCal->ftrV[CHX] *
1253  pthisMagCal->ftrV[CHY] +
1254  pthisMagCal->fA[CHX][CHZ] *
1255  pthisMagCal->ftrV[CHX] *
1256  pthisMagCal->ftrV[CHZ] +
1257  pthisMagCal->fA[CHY][CHZ] *
1258  pthisMagCal->ftrV[CHY] *
1259  pthisMagCal->ftrV[CHZ];
1260  ftmp *= 2.0F;
1261  ftmp -= pthisMagCal->fmatB[9][imin];
1262  ftmp += pthisMagCal->fA[CHX][CHX] *
1263  pthisMagCal->ftrV[CHX] *
1264  pthisMagCal->ftrV[CHX];
1265  ftmp += pthisMagCal->fA[CHY][CHY] *
1266  pthisMagCal->ftrV[CHY] *
1267  pthisMagCal->ftrV[CHY];
1268  ftmp += pthisMagCal->fA[CHZ][CHZ] *
1269  pthisMagCal->ftrV[CHZ] *
1270  pthisMagCal->ftrV[CHZ];
1271  pthisMagCal->ftrB = sqrtf(fabsf(ftmp));
1272 
1273  // calculate the normalized fit error as a percentage
1274  pthisMagCal->ftrFitErrorpc = 50.0F * sqrtf(fabsf(
1275  pthisMagCal->fvecA[imin] /
1276  (float) pthisMagBuffer->iMagBufferCount
1277  )) / (pthisMagCal->ftrB * pthisMagCal->ftrB);
1278 
1279  // convert the trial geomagnetic field strength B from counts to uT for un-normalized soft iron matrix A
1280  pthisMagCal->ftrB *= pthisMag->fuTPerCount;
1281 
1282  // compute the final hard iron offset (uT) by adding in previously subtracted zero mean offset (counts)
1283  for (i = CHX; i <= CHZ; i++)
1284  pthisMagCal->ftrV[i] =
1285  (
1286  pthisMagCal->ftrV[i] +
1287  (float) pthisMagCal->iMeanBs[i]
1288  ) *
1289  pthisMag->fuTPerCount;
1290 
1291  // normalize the ellipsoid matrix A to unit determinant
1292  ftmp = powf(fabs(fdetA), -(ONETHIRD));
1293  f3x3matrixAeqAxScalar(pthisMagCal->fA, ftmp);
1294 
1295  // each element of fA has been scaled by fdetA^(-1/3) so the square of geomagnetic field strength B^2
1296  // must be adjusted by the same amount and B by the square root to keep the ellipsoid equation valid:
1297  // (Bk-V)^T.A.(Bk-V) = (Bk-V)^T.(invW)^T.invW.(Bk-V) = B^2
1298  pthisMagCal->ftrB *= sqrt(fabs(ftmp));
1299 
1300  // prepare for eigendecomposition of fA to compute finvW from the square root of fA by setting
1301  // fmatA (upper left) to the 3x3 matrix fA, set fmatB (eigenvectors to identity matrix and
1302  // fvecA (eigenvalues) to diagonal elements of fmatA = fA
1303  for (i = 0; i < 3; i++)
1304  {
1305  for (j = 0; j < 3; j++)
1306  {
1307  pthisMagCal->fmatA[i][j] = pthisMagCal->fA[i][j];
1308  pthisMagCal->fmatB[i][j] = 0.0F;
1309  }
1310 
1311  pthisMagCal->fmatB[i][i] = 1.0F;
1312  pthisMagCal->fvecA[i] = pthisMagCal->fmatA[i][i];
1313  }
1314 
1315  // increment the time slice for the next iteration
1316  (pthisMagCal->itimeslice)++;
1317  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 48
1318 
1319  // repeating 3 time slices MAGBUFFSIZEX * MAGBUFFSIZEY + 49 to MAGBUFFSIZEX * MAGBUFFSIZEY + 51 inclusive
1320  // 9.7kticks = 0.2ms on KL25Z (with max stored in systick[6]).
1321  // perform the eigendecomposition of the 3x3 ellipsoid matrix fA which has 3 above diagonal elements: 2+1+0=3
1322  else if ((pthisMagCal->itimeslice >= (MAGBUFFSIZEX * MAGBUFFSIZEY + 49)) &&
1323  (pthisMagCal->itimeslice <= (MAGBUFFSIZEX * MAGBUFFSIZEY + 51)))
1324  {
1325  // set k to the matrix element of interest in range 0 to 2 to be zeroed and set row i and column j
1326  k = pthisMagCal->itimeslice - (MAGBUFFSIZEX * MAGBUFFSIZEY + 49);
1327  if (k == 0)
1328  {
1329  i = 0;
1330  j = 1;
1331  }
1332  else if (k == 1)
1333  {
1334  i = 0;
1335  j = 2;
1336  }
1337  else
1338  {
1339  i = 1;
1340  j = 2;
1341  }
1342 
1343  // only continue with eigen-decomposition if matrix element i, j has not already been zeroed
1344  if (fabsf(pthisMagCal->fmatA[i][j]) > 0.0F)
1345  fComputeEigSlice(pthisMagCal->fmatA, pthisMagCal->fmatB,
1346  pthisMagCal->fvecA, i, j, 3);
1347 
1348  // increment the time slice for the next iteration
1349  (pthisMagCal->itimeslice)++;
1350  } // end of time slices MAGBUFFSIZEX * MAGBUFFSIZEY + 49 to MAGBUFFSIZEX * MAGBUFFSIZEY + 51 inclusive
1351 
1352  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 52: 0.3k ticks = 0.006ms on KL25Z (constant) (stored in systick[7])
1353  // determine whether the eigen-decomposition of fA is complete
1354  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 52))
1355  {
1356  // sum residue of all above-diagonal elements and use to determine whether
1357  // to re-enter the eigen-decomposition or skip to calculation of the calibration coefficients
1358  fresidue = fabsf(pthisMagCal->fmatA[0][1]) + fabsf(pthisMagCal->fmatA[0][2]) + fabsf(pthisMagCal->fmatA[1][2]);
1359  if (fresidue > 0.0F)
1360  // continue the eigen-decomposition
1361  (pthisMagCal->itimeslice) = MAGBUFFSIZEX * MAGBUFFSIZEY + 49;
1362  else
1363  // continue to compute the calibration coefficients since the eigen-decomposition is complete
1364  (pthisMagCal->itimeslice)++;
1365  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 52
1366 
1367  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 53: 9.3k ticks = 0.19ms on KL25Z (constant) (stored in systick[8])
1368  // compute the inverse gain matrix invW from the eigendecomposition of the ellipsoid matrix A
1369  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 53))
1370  {
1371  // set pthisMagCal->fmatB to be eigenvectors . diag(sqrt(sqrt(eigenvalues))) = fmatB . diag(sqrt(sqrt(fvecA))
1372  for (j = 0; j < 3; j++) // loop over columns j
1373  {
1374  ftmp = sqrtf(sqrtf(fabsf(pthisMagCal->fvecA[j])));
1375  for (i = 0; i < 3; i++) // loop over rows i
1376  pthisMagCal->fmatB[i][j] *= ftmp;
1377  }
1378 
1379  // set ftrinvW to eigenvectors * diag(sqrt(eigenvalues)) * eigenvectors^T
1380  // = fmatB * fmatB^T = sqrt(fA) (guaranteed symmetric)
1381  // loop over on and above diagonal elements
1382  for (i = 0; i < 3; i++)
1383  {
1384  for (j = i; j < 3; j++)
1385  {
1386  pthisMagCal->ftrinvW[i][j] = pthisMagCal->ftrinvW[j][i] =
1387  pthisMagCal->fmatB[i][0] *
1388  pthisMagCal->fmatB[j][0] +
1389  pthisMagCal->fmatB[i][1] *
1390  pthisMagCal->fmatB[j][1] +
1391  pthisMagCal->fmatB[i][2] *
1392  pthisMagCal->fmatB[j][2];
1393  }
1394  }
1395 
1396  // reset the calibration in progress flag to allow writing to the magnetic buffer and flag
1397  // that a new 10 element calibration is available
1398  pthisMagCal->iCalInProgress = 0;
1399  pthisMagCal->iNewCalibrationAvailable = 10;
1400  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 53
1401 
1402  return;
1403 }
float ftrB
trial value of geomagnetic field magnitude in uT
Definition: magnetic.h:85
#define MAGBUFFSIZEX
x dimension in magnetometer buffer (12x24 equals 288 elements)
Definition: magnetic.h:44
#define CHY
Used to access Y-channel entries in various data data structures.
Definition: sensor_fusion.h:77
float fmatB[10][10]
scratch 10x10 float matrix used by calibration algorithms
Definition: magnetic.h:90
int32_t iMeanBs[3]
average magnetic measurement (counts)
Definition: magnetic.h:95
void f3x3matrixAeqMinusA(float A[][3])
function negates all elements of 3x3 matrix A
Definition: matrix.c:147
void f3x3matrixAeqAxScalar(float A[][3], float Scalar)
function multiplies all elements of 3x3 matrix A by the specified scalar
Definition: matrix.c:128
int8_t iNewCalibrationAvailable
flag denoting that a new calibration has been computed
Definition: magnetic.h:98
int8_t iInitiateMagCal
flag to start a new magnetic calibration
Definition: magnetic.h:99
int16_t iMagBufferCount
number of magnetometer readings
Definition: magnetic.h:68
float f3x3matrixDetA(float A[][3])
function calculates the determinant of a 3x3 matrix
Definition: matrix.c:209
int32_t int32
Definition: sensor_fusion.h:57
float ftrV[3]
trial value of hard iron offset z, y, z (uT)
Definition: magnetic.h:83
float ftrinvW[3][3]
trial inverse soft iron matrix size
Definition: magnetic.h:84
int32_t iSumBs[3]
sum of measurements in buffer (counts)
Definition: magnetic.h:94
float fmatA[10][10]
scratch 10x10 float matrix used by calibration algorithms
Definition: magnetic.h:89
float fvecA[10]
scratch 10x1 vector used by calibration algorithms
Definition: magnetic.h:91
float ftrFitErrorpc
trial value of fit error %
Definition: magnetic.h:86
int8_t iMagBufferReadOnly
flag to denote that the magnetic measurement buffer is temporarily read only
Definition: magnetic.h:100
float fuTPerCount
uT per count
#define ONETHIRD
one third
#define CHZ
float fA[3][3]
ellipsoid matrix A
Definition: magnetic.h:87
void f3x3matrixAeqInvSymB(float A[][3], float B[][3])
function directly calculates the symmetric inverse of a symmetric 3x3 matrix only the on and above di...
Definition: matrix.c:168
void fComputeEigSlice(float fmatA[10][10], float fmatB[10][10], float fvecA[10], int8 i, int8 j, int8 iMatrixSize)
Definition: matrix.c:572
#define CHX
Used to access X-channel entries in various data data structures.
Definition: sensor_fusion.h:76
#define MATRIX_10_SIZE
int8_t iCalInProgress
flag denoting that a calibration is in progress
Definition: magnetic.h:97
float finvA[3][3]
inverse of ellipsoid matrix A
Definition: magnetic.h:88
int32_t index[MAGBUFFSIZEX][MAGBUFFSIZEY]
array of time indices
Definition: magnetic.h:66
int16_t int16
Definition: sensor_fusion.h:56
#define MAGBUFFSIZEY
y dimension in magnetometer buffer (12x24 equals 288 elements)
Definition: magnetic.h:45
int8_t int8
Definition: sensor_fusion.h:55
int16_t iBs[3][MAGBUFFSIZEX][MAGBUFFSIZEY]
uncalibrated magnetometer readings
Definition: magnetic.h:65
int32_t itimeslice
counter for tine slicing magnetic calibration calculations
Definition: magnetic.h:96

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void fUpdateMagCalibration4 ( struct MagCalibration pthisMagCal,
struct MagBuffer pthisMagBuffer,
struct MagSensor pthisMag 
)
void fUpdateMagCalibration4Slice ( struct MagCalibration pthisMagCal,
struct MagBuffer pthisMagBuffer,
struct MagSensor pthisMag 
)

Definition at line 455 of file magnetic.c.

Referenced by fRunMagCalibration().

457 {
458  // local variables
459  int8 i,
460  j,
461  k; // loop counters
462 
463  // working arrays for 4x4 matrix inversion
464  float *pfRows[4];
465  int8 iColInd[4];
466  int8 iRowInd[4];
467  int8 iPivot[4];
468 
469  // reset the time slice to zero if iInitiateMagCal is set and then clear iInitiateMagCal
470  if (pthisMagCal->iInitiateMagCal)
471  {
472  pthisMagCal->itimeslice = 0;
473  pthisMagCal->iInitiateMagCal = false;
474  pthisMagCal->iMagBufferReadOnly = true;
475  }
476 
477  // time slice 0: 18.8K ticks = 0.39ms for 300 measurements on KL25Z
478  // initialize matrices and compute average of measurements in magnetic buffer
479  if (pthisMagCal->itimeslice == 0)
480  {
481  int16 iM; // number of measurements in the buffer
482 
483  // zero on and above diagonal matrix X^T.X (in fmatA), vector X^T.Y (in fvecA), scalar Y^T.Y (in fYTY)
484  pthisMagCal->fYTY = 0.0F;
485  for (i = 0; i < 4; i++)
486  {
487  pthisMagCal->fvecA[i] = 0.0F;
488  for (j = i; j < 4; j++) pthisMagCal->fmatA[i][j] = 0.0F;
489  }
490 
491  // zero total number of measurements and measurement sums
492  iM = 0;
493  for (i = 0; i < 3; i++) pthisMagCal->iSumBs[i] = 0;
494 
495  // compute the sum of measurements in the magnetic buffer
496  for (i = 0; i < MAGBUFFSIZEX; i++)
497  {
498  for (j = 0; j < MAGBUFFSIZEY; j++)
499  {
500  if (pthisMagBuffer->index[i][j] != -1)
501  {
502  iM++;
503  for (k = 0; k < 3; k++)
504  pthisMagCal->iSumBs[k] += (int32) pthisMagBuffer->iBs[k][i][j];
505  }
506  }
507  }
508 
509  // compute the magnetic buffer measurement averages with rounding
510  for (i = 0; i < 3; i++)
511  {
512  if (pthisMagCal->iSumBs[i] >= 0)
513  pthisMagCal->iMeanBs[i] =
514  (
515  pthisMagCal->iSumBs[i] +
516  ((int32) iM >> 1)
517  ) /
518  (int32) iM;
519  else
520  pthisMagCal->iMeanBs[i] =
521  (
522  pthisMagCal->iSumBs[i] -
523  ((int32) iM >> 1)
524  ) /
525  (int32) iM;
526  }
527 
528  // for defensive programming, re-store the number of active measurements in the buffer
529  pthisMagBuffer->iMagBufferCount = iM;
530 
531  // increment the time slice
532  (pthisMagCal->itimeslice)++;
533  } // end of time slice 0
534 
535  // time slices 1 to MAGBUFFSIZEX: each up to 81K ticks = 1.7ms
536  // accumulate the matrices fmatA=X^T.X and fvecA=X^T.Y and Y^T.Y from the magnetic buffer
537  else if ((pthisMagCal->itimeslice >= 1) &&
538  (pthisMagCal->itimeslice <= MAGBUFFSIZEX))
539  {
540  float fBsZeroMeanSq; // squared magnetic measurement (counts^2)
541  int32 iBsZeroMean[3]; // zero mean magnetic buffer measurement (counts)
542 
543  // accumulate the measurement matrix elements XTX (in fmatA), XTY (in fvecA) and YTY on the zero mean measurements
544  i = pthisMagCal->itimeslice - 1;
545  for (j = 0; j < MAGBUFFSIZEY; j++)
546  {
547  if (pthisMagBuffer->index[i][j] != -1)
548  {
549  // compute zero mean measurements
550  for (k = 0; k < 3; k++)
551  iBsZeroMean[k] = (int32) pthisMagBuffer->iBs[k][i][j] - (int32) pthisMagCal->iMeanBs[k];
552 
553  // accumulate the non-zero elements of zero mean XTX (in fmatA)
554  pthisMagCal->fmatA[0][0] += (float) (iBsZeroMean[0] * iBsZeroMean[0]);
555  pthisMagCal->fmatA[0][1] += (float) (iBsZeroMean[0] * iBsZeroMean[1]);
556  pthisMagCal->fmatA[0][2] += (float) (iBsZeroMean[0] * iBsZeroMean[2]);
557  pthisMagCal->fmatA[1][1] += (float) (iBsZeroMean[1] * iBsZeroMean[1]);
558  pthisMagCal->fmatA[1][2] += (float) (iBsZeroMean[1] * iBsZeroMean[2]);
559  pthisMagCal->fmatA[2][2] += (float) (iBsZeroMean[2] * iBsZeroMean[2]);
560 
561  // accumulate XTY (in fvecA)
562  fBsZeroMeanSq = (float)
563  (
564  iBsZeroMean[CHX] *
565  iBsZeroMean[CHX] +
566  iBsZeroMean[CHY] *
567  iBsZeroMean[CHY] +
568  iBsZeroMean[CHZ] *
569  iBsZeroMean[CHZ]
570  );
571  for (k = 0; k < 3; k++)
572  pthisMagCal->fvecA[k] += (float) iBsZeroMean[k] * fBsZeroMeanSq;
573  pthisMagCal->fvecA[3] += fBsZeroMeanSq;
574 
575  // accumulate fYTY
576  pthisMagCal->fYTY += fBsZeroMeanSq * fBsZeroMeanSq;
577  }
578  }
579 
580  // increment the time slice
581  (pthisMagCal->itimeslice)++;
582  } // end of time slices 1 to MAGBUFFSIZEX
583 
584  // time slice MAGBUFFSIZEX+1: 18.3K ticks = 0.38ms on KL25Z (constant) (stored in systick[2])
585  // re-enable magnetic buffer for writing and invert fmatB = fmatA = X^T.X in situ
586  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX + 1))
587  {
588  int8 ierror; // matrix inversion error flag
589 
590  // set fmatA[3][3] = X^T.X[3][3] to number of measurements found
591  pthisMagCal->fmatA[3][3] = (float) pthisMagBuffer->iMagBufferCount;
592 
593  // enable the magnetic buffer for writing now that the matrices have been computed
594  pthisMagCal->iMagBufferReadOnly = false;
595 
596  // set fmatA and fmatB to above diagonal elements of fmatA
597  for (i = 0; i < 4; i++)
598  {
599  for (j = 0; j <= i; j++)
600  pthisMagCal->fmatB[i][j] = pthisMagCal->fmatB[j][i] = pthisMagCal->fmatA[i][j] = pthisMagCal->fmatA[j][i];
601  }
602 
603  // set fmatB = inv(fmatB) = inv(X^T.X)
604  for (i = 0; i < 4; i++) pfRows[i] = pthisMagCal->fmatB[i];
605  fmatrixAeqInvA(pfRows, iColInd, iRowInd, iPivot, 4, &ierror);
606 
607  // increment the time slice
608  (pthisMagCal->itimeslice)++;
609  } // // end of time slice MAGBUFFSIZEX+1
610 
611  // time slice MAGBUFFSIZEX+2: 17.2K ticks = 0.36ms on KL25Z (constant) (stored in systick[3])
612  // compute the solution vector and the calibration coefficients
613  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX + 2))
614  {
615  float fE; // error function = r^T.r
616  float ftmp; // scratch
617 
618  // the trial inverse soft iron matrix invW always equals the identity matrix for 4 element calibration
619  f3x3matrixAeqI(pthisMagCal->ftrinvW);
620 
621  // calculate solution vector fvecB = beta (4x1) = inv(X^T.X).X^T.Y = fmatB * fvecA (counts)
622  for (i = 0; i < 4; i++)
623  {
624  pthisMagCal->fvecB[i] = pthisMagCal->fmatB[i][0] * pthisMagCal->fvecA[0];
625  for (j = 1; j < 4; j++)
626  pthisMagCal->fvecB[i] += pthisMagCal->fmatB[i][j] * pthisMagCal->fvecA[j];
627  }
628 
629  // compute the hard iron vector (uT) correction for zero mean data
630  ftmp = 0.5F * pthisMag->fuTPerCount;
631  for (i = CHX; i <= CHZ; i++)
632  pthisMagCal->ftrV[i] = ftmp * pthisMagCal->fvecB[i];
633 
634  // compute the geomagnetic field strength B (uT)
635  pthisMagCal->ftrB = pthisMagCal->fvecB[3] *
636  pthisMag->fuTPerCount *
637  pthisMag->fuTPerCount;
638  for (i = CHX; i <= CHZ; i++)
639  pthisMagCal->ftrB += pthisMagCal->ftrV[i] * pthisMagCal->ftrV[i];
640  pthisMagCal->ftrB = sqrtf(fabs(pthisMagCal->ftrB));
641 
642  // add in the previously subtracted magnetic buffer mean to get true hard iron offset (uT)
643  ftmp = pthisMag->fuTPerCount / (float) pthisMagBuffer->iMagBufferCount;
644  for (i = CHX; i <= CHZ; i++)
645  pthisMagCal->ftrV[i] += (float) pthisMagCal->iSumBs[i] * ftmp;
646 
647  // calculate E = r^T.r = Y^T.Y - 2 * beta^T.(X^T.Y) + beta^T.(X^T.X).beta
648  // set E = beta^T.(X^T.Y) = fvecB^T.fvecA
649  fE = pthisMagCal->fvecB[0] * pthisMagCal->fvecA[0];
650  for (i = 1; i < 4; i++)
651  fE += pthisMagCal->fvecB[i] * pthisMagCal->fvecA[i];
652 
653  // set E = YTY - 2 * beta^T.(X^T.Y) = YTY - 2 * E;
654  fE = pthisMagCal->fYTY - 2.0F * fE;
655 
656  // set fvecA = (X^T.X).beta = fmatA.fvecB
657  for (i = 0; i < 4; i++)
658  {
659  pthisMagCal->fvecA[i] = pthisMagCal->fmatA[i][0] * pthisMagCal->fvecB[0];
660  for (j = 1; j < 4; j++)
661  pthisMagCal->fvecA[i] += pthisMagCal->fmatA[i][j] * pthisMagCal->fvecB[j];
662  }
663 
664  // add beta^T.(X^T.X).beta = fvecB^T * fvecA to give fit error E (un-normalized counts^4)
665  for (i = 0; i < 4; i++)
666  fE += pthisMagCal->fvecB[i] * pthisMagCal->fvecA[i];
667 
668  // normalize fit error to the number of measurements and take square root to get error in uT^2
669  fE = sqrtf(fabs(fE) / (float) pthisMagBuffer->iMagBufferCount) *
670  pthisMag->fuTPerCount *
671  pthisMag->fuTPerCount;
672 
673  // obtain dimensionless error by dividing square square of the geomagnetic field and convert to percent
674  pthisMagCal->ftrFitErrorpc = 50.0F * fE / (pthisMagCal->ftrB * pthisMagCal->ftrB);
675 
676  // reset the calibration in progress flag to allow writing to the magnetic buffer and flag
677  // that a new 4 element calibration is available
678  pthisMagCal->iCalInProgress = 0;
679  pthisMagCal->iNewCalibrationAvailable = 4;
680  } // end of time slice MAGBUFFSIZEX+2
681 
682  return;
683 }
float ftrB
trial value of geomagnetic field magnitude in uT
Definition: magnetic.h:85
void fmatrixAeqInvA(float *A[], int8 iColInd[], int8 iRowInd[], int8 iPivot[], int8 isize, int8 *pierror)
function uses Gauss-Jordan elimination to compute the inverse of matrix A in situ on exit...
Definition: matrix.c:666
#define MAGBUFFSIZEX
x dimension in magnetometer buffer (12x24 equals 288 elements)
Definition: magnetic.h:44
#define CHY
Used to access Y-channel entries in various data data structures.
Definition: sensor_fusion.h:77
float fmatB[10][10]
scratch 10x10 float matrix used by calibration algorithms
Definition: magnetic.h:90
int32_t iMeanBs[3]
average magnetic measurement (counts)
Definition: magnetic.h:95
float fvecB[4]
scratch 4x1 vector used by calibration algorithms
Definition: magnetic.h:92
int8_t iNewCalibrationAvailable
flag denoting that a new calibration has been computed
Definition: magnetic.h:98
int8_t iInitiateMagCal
flag to start a new magnetic calibration
Definition: magnetic.h:99
int16_t iMagBufferCount
number of magnetometer readings
Definition: magnetic.h:68
int32_t int32
Definition: sensor_fusion.h:57
float ftrV[3]
trial value of hard iron offset z, y, z (uT)
Definition: magnetic.h:83
float ftrinvW[3][3]
trial inverse soft iron matrix size
Definition: magnetic.h:84
int32_t iSumBs[3]
sum of measurements in buffer (counts)
Definition: magnetic.h:94
float fmatA[10][10]
scratch 10x10 float matrix used by calibration algorithms
Definition: magnetic.h:89
float fvecA[10]
scratch 10x1 vector used by calibration algorithms
Definition: magnetic.h:91
float ftrFitErrorpc
trial value of fit error %
Definition: magnetic.h:86
int8_t iMagBufferReadOnly
flag to denote that the magnetic measurement buffer is temporarily read only
Definition: magnetic.h:100
float fuTPerCount
uT per count
#define CHZ
#define CHX
Used to access X-channel entries in various data data structures.
Definition: sensor_fusion.h:76
void f3x3matrixAeqI(float A[][3])
function sets the 3x3 matrix A to the identity matrix
Definition: matrix.c:45
int8_t iCalInProgress
flag denoting that a calibration is in progress
Definition: magnetic.h:97
float fYTY
Y^T.Y for 4 element calibration = (iB^2)^2.
Definition: magnetic.h:93
int32_t index[MAGBUFFSIZEX][MAGBUFFSIZEY]
array of time indices
Definition: magnetic.h:66
int16_t int16
Definition: sensor_fusion.h:56
#define MAGBUFFSIZEY
y dimension in magnetometer buffer (12x24 equals 288 elements)
Definition: magnetic.h:45
int8_t int8
Definition: sensor_fusion.h:55
int16_t iBs[3][MAGBUFFSIZEX][MAGBUFFSIZEY]
uncalibrated magnetometer readings
Definition: magnetic.h:65
int32_t itimeslice
counter for tine slicing magnetic calibration calculations
Definition: magnetic.h:96

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void fUpdateMagCalibration7 ( struct MagCalibration pthisMagCal,
struct MagBuffer pthisMagBuffer,
struct MagSensor pthisMag 
)
void fUpdateMagCalibration7Slice ( struct MagCalibration pthisMagCal,
struct MagBuffer pthisMagBuffer,
struct MagSensor pthisMag 
)

Definition at line 686 of file magnetic.c.

Referenced by fRunMagCalibration().

688 {
689  // local variables
690  float fresidue; // eigen-decomposition residual sum
691  float ftmp; // scratch variable
692  int8 i,
693  j,
694  k,
695  l; // loop counters
696 #define MATRIX_7_SIZE 7
697  // reset the time slice to zero if iInitiateMagCal is set and then clear iInitiateMagCal
698  if (pthisMagCal->iInitiateMagCal)
699  {
700  pthisMagCal->itimeslice = 0;
701  pthisMagCal->iInitiateMagCal = false;
702  pthisMagCal->iMagBufferReadOnly = true;
703  }
704 
705  // time slice 0: 18.1K KL25Z ticks for 300 measurements = 0.38ms on KL25Z (variable) stored in systick[0]
706  // zero measurement matrix and calculate the mean values in the magnetic buffer
707  if (pthisMagCal->itimeslice == 0)
708  {
709  int16 iM; // number of measurements in the magnetic buffer
710 
711  // zero the on and above diagonal elements of the 7x7 symmetric measurement matrix fmatA
712  for (i = 0; i < MATRIX_7_SIZE; i++)
713  for (j = i; j < MATRIX_7_SIZE; j++)
714  pthisMagCal->fmatA[i][j] = 0.0F;
715 
716  // compute the sum of measurements in the magnetic buffer
717  iM = 0;
718  for (i = 0; i < 3; i++) pthisMagCal->iSumBs[i] = 0;
719  for (i = 0; i < MAGBUFFSIZEX; i++)
720  {
721  for (j = 0; j < MAGBUFFSIZEY; j++)
722  {
723  if (pthisMagBuffer->index[i][j] != -1)
724  {
725  iM++;
726  for (k = 0; k < 3; k++)
727  pthisMagCal->iSumBs[k] += (int32) pthisMagBuffer->iBs[k][i][j];
728  }
729  }
730  }
731 
732  // compute the magnetic buffer measurement averages with nearest integer rounding
733  for (i = 0; i < 3; i++)
734  {
735  if (pthisMagCal->iSumBs[i] >= 0)
736  pthisMagCal->iMeanBs[i] =
737  (
738  pthisMagCal->iSumBs[i] +
739  ((int32) iM >> 1)
740  ) /
741  (int32) iM;
742  else
743  pthisMagCal->iMeanBs[i] =
744  (
745  pthisMagCal->iSumBs[i] -
746  ((int32) iM >> 1)
747  ) /
748  (int32) iM;
749  }
750 
751  // as defensive programming also ensure the number of measurements found is re-stored
752  pthisMagBuffer->iMagBufferCount = iM;
753 
754  // increment the time slice for the next iteration
755  (pthisMagCal->itimeslice)++;
756  } // end of time slice 0
757 
758  // time slices 1 to MAGBUFFSIZEX * MAGBUFFSIZEY: accumulate matrices: 8.6K KL25Z ticks = 0.18ms (with max stored in systick[1])
759  else if ((pthisMagCal->itimeslice >= 1) &&
760  (pthisMagCal->itimeslice <= MAGBUFFSIZEX * MAGBUFFSIZEY))
761  {
762  // accumulate the symmetric matrix fmatA on the zero mean measurements
763  i = (pthisMagCal->itimeslice - 1) / MAGBUFFSIZEY; // matrix row i ranges 0 to MAGBUFFSIZEX-1
764  j = (pthisMagCal->itimeslice - 1) % MAGBUFFSIZEY; // matrix column j ranges 0 to MAGBUFFSIZEY-1
765  if (pthisMagBuffer->index[i][j] != -1)
766  {
767  // set fvecA to be vector of zero mean measurements and their squares
768  for (k = 0; k < 3; k++)
769  {
770  pthisMagCal->fvecA[k + 3] = (float)
771  (
772  (int32) pthisMagBuffer->iBs[k][i][j] -
773  (int32) pthisMagCal->iMeanBs[k]
774  );
775  pthisMagCal->fvecA[k] = pthisMagCal->fvecA[k + 3] * pthisMagCal->fvecA[k + 3];
776  }
777 
778  // update non-zero elements fmatA[0-2][6] of fmatA ignoring fmatA[6][6] which is set later.
779  // elements fmatA[3-5][6] are zero as a result of subtracting the mean value
780  for (k = 0; k < 3; k++)
781  pthisMagCal->fmatA[k][6] += pthisMagCal->fvecA[k];
782 
783  // update the remaining on and above diagonal elements fmatA[0-5][0-5]
784  for (k = 0; k < (MATRIX_7_SIZE - 1); k++)
785  {
786  for (l = k; l < (MATRIX_7_SIZE - 1); l++)
787  pthisMagCal->fmatA[k][l] += pthisMagCal->fvecA[k] * pthisMagCal->fvecA[l];
788  }
789  }
790 
791  // increment the time slice for the next iteration
792  (pthisMagCal->itimeslice)++;
793  } // end of time slices 1 to MAGBUFFSIZEX * MAGBUFFSIZEY
794 
795  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 1: 0.8K ticks on KL25Z = 0.02ms on KL25Z (constant) (stored in systick[2])
796  // re-enable magnetic buffer for writing and prepare fmatA, fmatB, fvecA for eigendecomposition
797  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 1))
798  {
799  // set fmatA[6][6] to the number of magnetic measurements found
800  pthisMagCal->fmatA[MATRIX_7_SIZE - 1][MATRIX_7_SIZE - 1] = (float) pthisMagBuffer->iMagBufferCount;
801 
802  // clear the magnetic buffer read only flag now that the matrices have been computed
803  pthisMagCal->iMagBufferReadOnly = false;
804 
805  // set below diagonal elements of 7x7 matrix fmatA to above diagonal elements
806  for (i = 1; i < MATRIX_7_SIZE; i++)
807  for (j = 0; j < i; j++)
808  pthisMagCal->fmatA[i][j] = pthisMagCal->fmatA[j][i];
809 
810  // set matrix of eigenvectors fmatB to identity matrix and eigenvalues vector fvecA to diagonal elements of fmatA
811  for (i = 0; i < MATRIX_7_SIZE; i++)
812  {
813  for (j = 0; j < MATRIX_7_SIZE; j++)
814  pthisMagCal->fmatB[i][j] = 0.0F;
815  pthisMagCal->fmatB[i][i] = 1.0F;
816  pthisMagCal->fvecA[i] = pthisMagCal->fmatA[i][i];
817  }
818 
819  // increment the time slice for the next iteration
820  (pthisMagCal->itimeslice)++;
821  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 1
822 
823  // repeating 21 time slices MAGBUFFSIZEX * MAGBUFFSIZEY + 2 to MAGBUFFSIZEX * MAGBUFFSIZEY + 22 inclusive
824  // to perform the eigendecomposition of the measurement matrix fmatA.
825  // 19.6k ticks = 0.41ms on KL25Z (with max stored in systick[3]).
826  // for a 7x7 matrix there are 21 above diagonal elements: 6+5+4+3+2+1+0=21
827  else if ((pthisMagCal->itimeslice >= (MAGBUFFSIZEX * MAGBUFFSIZEY + 2)) &&
828  (pthisMagCal->itimeslice <= (MAGBUFFSIZEX * MAGBUFFSIZEY + 22)))
829  {
830  // set k to the matrix element in range 0 to 20 to be zeroed and used it to set row i and column j
831  k = pthisMagCal->itimeslice - (MAGBUFFSIZEX * MAGBUFFSIZEY + 2);
832  if (k < 6)
833  {
834  i = 0;
835  j = k + 1;
836  }
837  else if (k < 11)
838  {
839  i = 1;
840  j = k - 4;
841  }
842  else if (k < 15)
843  {
844  i = 2;
845  j = k - 8;
846  }
847  else if (k < 18)
848  {
849  i = 3;
850  j = k - 11;
851  }
852  else if (k < 20)
853  {
854  i = 4;
855  j = k - 13;
856  }
857  else
858  {
859  i = 5;
860  j = 6;
861  }
862 
863  // only continue if matrix element i, j has not already been zeroed
864  if (fabsf(pthisMagCal->fmatA[i][j]) > 0.0F)
865  fComputeEigSlice(pthisMagCal->fmatA, pthisMagCal->fmatB,
866  pthisMagCal->fvecA, i, j, MATRIX_7_SIZE);
867 
868  // increment the time slice for the next iteration
869  (pthisMagCal->itimeslice)++;
870  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 2 to MAGBUFFSIZEX * MAGBUFFSIZEY + 22 inclusive
871 
872  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 23: 2.6k ticks on KL25Z = 0.05ms on KL25Z (constant) (stored in systick[4])
873  // compute the sum of the absolute values of above diagonal elements in fmatA as eigen-decomposition exit criterion
874  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 23))
875  {
876  // sum residue of all above-diagonal elements
877  fresidue = 0.0F;
878  for (i = 0; i < MATRIX_7_SIZE; i++)
879  for (j = i + 1; j < MATRIX_7_SIZE; j++)
880  fresidue += fabsf(pthisMagCal->fmatA[i][j]);
881 
882  // determine whether to re-enter the eigen-decomposition or skip to calculation of the calibration coefficients
883  if (fresidue > 0.0F)
884  // continue the eigen-decomposition
885  (pthisMagCal->itimeslice) = MAGBUFFSIZEX * MAGBUFFSIZEY + 2;
886  else
887  // continue to compute the calibration coefficients since the eigen-decomposition is complete
888  (pthisMagCal->itimeslice)++;
889  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 23
890 
891  // time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 24: 27.8k ticks = 0.58ms on KL25Z (constant) (stored in systick[5])
892  // compute the calibration coefficients from the solution eigenvector
893  else if (pthisMagCal->itimeslice == (MAGBUFFSIZEX * MAGBUFFSIZEY + 24))
894  {
895  float fdetA; // determinant of ellipsoid matrix A
896  int8 imin; // column of solution eigenvector with minimum eigenvalue
897 
898  // set imin to the index of the smallest eigenvalue in fvecA
899  imin = 0;
900  for (i = 1; i < MATRIX_7_SIZE; i++)
901  if (pthisMagCal->fvecA[i] < pthisMagCal->fvecA[imin]) imin = i;
902 
903  // the ellipsoid fA must have positive determinant but the eigensolver can equally easily return a negated
904  // normalized eigenvector without changing the eigenvalue. compute the determinant of ellipsoid matrix A
905  // from first three elements of eigenvector and negate the eigenvector if negative.
906  fdetA = pthisMagCal->fmatB[CHX][imin] *
907  pthisMagCal->fmatB[CHY][imin] *
908  pthisMagCal->fmatB[CHZ][imin];
909  if (fdetA < 0.0F)
910  {
911  fdetA = fabs(fdetA);
912  for (i = 0; i < MATRIX_7_SIZE; i++)
913  pthisMagCal->fmatB[i][imin] = -pthisMagCal->fmatB[i][imin];
914  }
915 
916  // set diagonal elements of ellipsoid matrix A to the solution vector imin with smallest eigenvalue and
917  // zero off-diagonal elements since these are always zero in the 7 element model
918  // compute the hard iron offset fV for zero mean data (counts)
919  for (i = CHX; i <= CHZ; i++)
920  pthisMagCal->ftrV[i] = -0.5F *
921  pthisMagCal->fmatB[i + 3][imin] /
922  pthisMagCal->fmatB[i][imin];
923 
924  // set ftmp to the square of the geomagnetic field strength fB (counts) corresponding to current value of fdetA
925  ftmp = -pthisMagCal->fmatB[6][imin];
926  for (i = CHX; i <= CHZ; i++)
927  ftmp += pthisMagCal->fmatB[i][imin] *
928  pthisMagCal->ftrV[i] *
929  pthisMagCal->ftrV[i];
930 
931  // calculate the trial normalized fit error as a percentage normalized by the geomagnetic field strength squared
932  pthisMagCal->ftrFitErrorpc = 50.0F * sqrtf(fabsf(pthisMagCal->fvecA[imin]) /
933  (float) pthisMagBuffer->iMagBufferCount) / fabsf(ftmp);
934 
935  // compute the geomagnetic field strength (uT) for current value of fdetA
936  pthisMagCal->ftrB = sqrtf(fabsf(ftmp)) * pthisMag->fuTPerCount;
937 
938  // compute the normalized ellipsoid matrix A with unit determinant and derive invW also with unit determinant.
939  ftmp = powf(fabs(fdetA), -(ONETHIRD));
940  for (i = CHX; i <= CHZ; i++)
941  {
942  pthisMagCal->fA[i][i] = pthisMagCal->fmatB[i][imin] * ftmp;
943  pthisMagCal->ftrinvW[i][i] = sqrtf(fabsf(pthisMagCal->fA[i][i]));
944  }
945 
946  pthisMagCal->fA[CHX][CHY] = pthisMagCal->fA[CHX][CHZ] = pthisMagCal->fA[CHY][CHZ] = 0.0F;
947  pthisMagCal->ftrinvW[CHX][CHY] = pthisMagCal->ftrinvW[CHX][CHZ] = pthisMagCal->ftrinvW[CHY][CHZ] = 0.0F;
948 
949  // each element of fA has been scaled by fdetA^(-1/3) so the square of geomagnetic field strength B^2
950  // must be adjusted by the same amount and B by the square root to keep the ellipsoid equation valid:
951  // (Bk-V)^T.A.(Bk-V) = (Bk-V)^T.(invW)^T.invW.(Bk-V) = B^2
952  pthisMagCal->ftrB *= sqrt(fabs(ftmp));
953 
954  // compute the final hard iron offset (uT) by adding in previously subtracted zero mean offset (counts)
955  for (i = CHX; i <= CHZ; i++)
956  pthisMagCal->ftrV[i] =
957  (
958  pthisMagCal->ftrV[i] +
959  (float) pthisMagCal->iMeanBs[i]
960  ) *
961  pthisMag->fuTPerCount;
962 
963  // reset the calibration in progress flag to allow writing to the magnetic buffer and flag
964  // that a new 7 element calibration is available
965  pthisMagCal->iCalInProgress = 0;
966  pthisMagCal->iNewCalibrationAvailable = 7;
967  } // end of time slice MAGBUFFSIZEX * MAGBUFFSIZEY + 24
968 
969  return;
970 }
float ftrB
trial value of geomagnetic field magnitude in uT
Definition: magnetic.h:85
#define MAGBUFFSIZEX
x dimension in magnetometer buffer (12x24 equals 288 elements)
Definition: magnetic.h:44
#define CHY
Used to access Y-channel entries in various data data structures.
Definition: sensor_fusion.h:77
float fmatB[10][10]
scratch 10x10 float matrix used by calibration algorithms
Definition: magnetic.h:90
int32_t iMeanBs[3]
average magnetic measurement (counts)
Definition: magnetic.h:95
int8_t iNewCalibrationAvailable
flag denoting that a new calibration has been computed
Definition: magnetic.h:98
int8_t iInitiateMagCal
flag to start a new magnetic calibration
Definition: magnetic.h:99
int16_t iMagBufferCount
number of magnetometer readings
Definition: magnetic.h:68
int32_t int32
Definition: sensor_fusion.h:57
float ftrV[3]
trial value of hard iron offset z, y, z (uT)
Definition: magnetic.h:83
float ftrinvW[3][3]
trial inverse soft iron matrix size
Definition: magnetic.h:84
int32_t iSumBs[3]
sum of measurements in buffer (counts)
Definition: magnetic.h:94
float fmatA[10][10]
scratch 10x10 float matrix used by calibration algorithms
Definition: magnetic.h:89
float fvecA[10]
scratch 10x1 vector used by calibration algorithms
Definition: magnetic.h:91
float ftrFitErrorpc
trial value of fit error %
Definition: magnetic.h:86
int8_t iMagBufferReadOnly
flag to denote that the magnetic measurement buffer is temporarily read only
Definition: magnetic.h:100
float fuTPerCount
uT per count
#define MATRIX_7_SIZE
#define ONETHIRD
one third
#define CHZ
float fA[3][3]
ellipsoid matrix A
Definition: magnetic.h:87
void fComputeEigSlice(float fmatA[10][10], float fmatB[10][10], float fvecA[10], int8 i, int8 j, int8 iMatrixSize)
Definition: matrix.c:572
#define CHX
Used to access X-channel entries in various data data structures.
Definition: sensor_fusion.h:76
int8_t iCalInProgress
flag denoting that a calibration is in progress
Definition: magnetic.h:97
int32_t index[MAGBUFFSIZEX][MAGBUFFSIZEY]
array of time indices
Definition: magnetic.h:66
int16_t int16
Definition: sensor_fusion.h:56
#define MAGBUFFSIZEY
y dimension in magnetometer buffer (12x24 equals 288 elements)
Definition: magnetic.h:45
int8_t int8
Definition: sensor_fusion.h:55
int16_t iBs[3][MAGBUFFSIZEX][MAGBUFFSIZEY]
uncalibrated magnetometer readings
Definition: magnetic.h:65
int32_t itimeslice
counter for tine slicing magnetic calibration calculations
Definition: magnetic.h:96

+ Here is the call graph for this function:

+ Here is the caller graph for this function:

void iUpdateMagBuffer ( struct MagBuffer pthisMagBuffer,
struct MagSensor pthisMag,
int32_t  loopcounter 
)

Definition at line 103 of file magnetic.c.

Referenced by processMagData().

105 {
106  // local variables
107  int32 idelta; // absolute vector distance
108  int32 i; // counter
109  int16 itanj,
110  itank; // indexing accelerometer ratios
111  int8 j,
112  k,
113  l,
114  m; // counters
115  int8 itooclose; // flag denoting measurement is too close to existing ones
116 
117  // calculate the magnetometer buffer bins from the tangent ratios
118  if (pthisMag->iBc[CHZ] == 0) return;
119  itanj = (100 * (int32) pthisMag->iBc[CHX]) / ((int32) pthisMag->iBc[CHZ]);
120  itank = (100 * (int32) pthisMag->iBc[CHY]) / ((int32) pthisMag->iBc[CHZ]);
121 
122  // map tangent ratios to bins j and k using equal angle bins: C guarantees left to right execution of the test
123  // and add an offset of MAGBUFFSIZEX bins to k to mimic atan2 on this ratio
124  // j will vary from 0 to MAGBUFFSIZEX - 1 and k from 0 to 2 * MAGBUFFSIZEX - 1
125  j = k = 0;
126  while ((j < (MAGBUFFSIZEX - 1) && (itanj >= pthisMagBuffer->tanarray[j])))
127  j++;
128  while ((k < (MAGBUFFSIZEX - 1) && (itank >= pthisMagBuffer->tanarray[k])))
129  k++;
130  if (pthisMag->iBc[CHX] < 0) k += MAGBUFFSIZEX;
131 
132  // case 1: buffer is full and this bin has a measurement: over-write without increasing number of measurements
133  // this is the most common option at run time
134  if ((pthisMagBuffer->iMagBufferCount == MAXMEASUREMENTS) &&
135  (pthisMagBuffer->index[j][k] != -1))
136  {
137  // store the fast (unaveraged at typically 200Hz) integer magnetometer reading into the buffer bin j, k
138  for (i = CHX; i <= CHZ; i++)
139  {
140  pthisMagBuffer->iBs[i][j][k] = pthisMag->iBs[i];
141  }
142 
143  pthisMagBuffer->index[j][k] = loopcounter;
144  return;
145  } // end case 1
146 
147  // case 2: the buffer is full and this bin does not have a measurement: store and retire the oldest
148  // this is the second most common option at run time
149  if ((pthisMagBuffer->iMagBufferCount == MAXMEASUREMENTS) &&
150  (pthisMagBuffer->index[j][k] == -1))
151  {
152  // store the fast (unaveraged at typically 200Hz) integer magnetometer reading into the buffer bin j, k
153  for (i = CHX; i <= CHZ; i++)
154  {
155  pthisMagBuffer->iBs[i][j][k] = pthisMag->iBs[i];
156  }
157 
158  pthisMagBuffer->index[j][k] = loopcounter;
159 
160  // set l and m to the oldest active entry and disable it
161  i = loopcounter;
162  l = m = 0; // to avoid compiler complaint
163  for (j = 0; j < MAGBUFFSIZEX; j++)
164  {
165  for (k = 0; k < MAGBUFFSIZEY; k++)
166  {
167  // check if the time stamp is older than the oldest found so far (normally fails this test)
168  if (pthisMagBuffer->index[j][k] < i)
169  {
170  // check if this bin is active (normally passes this test)
171  if (pthisMagBuffer->index[j][k] != -1)
172  {
173  // set l and m to the indices of the oldest entry found so far
174  l = j;
175  m = k;
176 
177  // set i to the time stamp of the oldest entry found so far
178  i = pthisMagBuffer->index[l][m];
179  } // end of test for active
180  } // end of test for older
181  } // end of loop over k
182  } // end of loop over j
183 
184  // deactivate the oldest measurement (no need to zero the measurement data)
185  pthisMagBuffer->index[l][m] = -1;
186  return;
187  } // end case 2
188 
189  // case 3: buffer is not full and this bin is empty: store and increment number of measurements
190  if ((pthisMagBuffer->iMagBufferCount < MAXMEASUREMENTS) &&
191  (pthisMagBuffer->index[j][k] == -1))
192  {
193  // store the fast (unaveraged at typically 200Hz) integer magnetometer reading into the buffer bin j, k
194  for (i = CHX; i <= CHZ; i++)
195  {
196  pthisMagBuffer->iBs[i][j][k] = pthisMag->iBs[i];
197  }
198 
199  pthisMagBuffer->index[j][k] = loopcounter;
200  (pthisMagBuffer->iMagBufferCount)++;
201  return;
202  } // end case 3
203 
204  // case 4: buffer is not full and this bin has a measurement: over-write if close or try to slot in
205  // elsewhere if not close to the other measurements so as to create a mesh at power up
206  if ((pthisMagBuffer->iMagBufferCount < MAXMEASUREMENTS) &&
207  (pthisMagBuffer->index[j][k] != -1))
208  {
209  // calculate the vector difference between current measurement and the buffer entry
210  idelta = 0;
211  for (i = CHX; i <= CHZ; i++)
212  {
213  idelta += abs((int32) pthisMag->iBs[i] -
214  (int32) pthisMagBuffer->iBs[i][j][k]);
215  }
216 
217  // check to see if the current reading is close to this existing magnetic buffer entry
218  if (idelta < MESHDELTACOUNTS)
219  {
220  // simply over-write the measurement and return
221  for (i = CHX; i <= CHZ; i++)
222  {
223  pthisMagBuffer->iBs[i][j][k] = pthisMag->iBs[i];
224  }
225 
226  pthisMagBuffer->index[j][k] = loopcounter;
227  }
228  else
229  {
230  // reset the flag denoting that the current measurement is close to any measurement in the buffer
231  itooclose = 0;
232 
233  // to avoid compiler warning
234  l = m = 0;
235 
236  // loop over the buffer j from 0 potentially up to MAGBUFFSIZEX - 1
237  j = 0;
238  while (!itooclose && (j < MAGBUFFSIZEX))
239  {
240  // loop over the buffer k from 0 potentially up to MAGBUFFSIZEY - 1
241  k = 0;
242  while (!itooclose && (k < MAGBUFFSIZEY))
243  {
244  // check whether this buffer entry already has a measurement or not
245  if (pthisMagBuffer->index[j][k] != -1)
246  {
247  // calculate the vector difference between current measurement and the buffer entry
248  idelta = 0;
249  for (i = CHX; i <= CHZ; i++)
250  {
251  idelta += abs((int32) pthisMag->iBs[i] -
252  (int32) pthisMagBuffer->iBs[i][j][k]);
253  }
254 
255  // check to see if the current reading is close to this existing magnetic buffer entry
256  if (idelta < MESHDELTACOUNTS)
257  {
258  // set the flag to abort the search
259  itooclose = 1;
260  }
261  }
262  else
263  {
264  // store the location of this empty bin for future use
265  l = j;
266  m = k;
267  } // end of test for valid measurement in this bin
268 
269  k++;
270  } // end of k loop
271 
272  j++;
273  } // end of j loop
274 
275  // if none too close, store the measurement in the last empty bin found and return
276  // l and m are guaranteed to be set if no entries too close are detected
277  if (!itooclose)
278  {
279  for (i = CHX; i <= CHZ; i++)
280  {
281  pthisMagBuffer->iBs[i][l][m] = pthisMag->iBs[i];
282  }
283 
284  pthisMagBuffer->index[l][m] = loopcounter;
285  (pthisMagBuffer->iMagBufferCount)++;
286  }
287  } // end of test for closeness to current buffer entry
288 
289  return;
290  } // end case 4
291 
292  // this line should be unreachable
293  return;
294 }
#define MAGBUFFSIZEX
x dimension in magnetometer buffer (12x24 equals 288 elements)
Definition: magnetic.h:44
#define CHY
Used to access Y-channel entries in various data data structures.
Definition: sensor_fusion.h:77
int16_t iBs[3]
averaged uncalibrated measurement (counts)
int16_t tanarray[MAGBUFFSIZEX-1]
array of tangents of (100 * angle)
Definition: magnetic.h:67
int16_t iMagBufferCount
number of magnetometer readings
Definition: magnetic.h:68
int32_t int32
Definition: sensor_fusion.h:57
#define MAXMEASUREMENTS
maximum number of measurements used for calibration
Definition: magnetic.h:49
#define CHZ
int16_t iBc[3]
averaged calibrated measurement (counts)
#define CHX
Used to access X-channel entries in various data data structures.
Definition: sensor_fusion.h:76
#define MESHDELTACOUNTS
magnetic buffer mesh spacing in counts (here 5uT)
Definition: magnetic.h:54
int32_t index[MAGBUFFSIZEX][MAGBUFFSIZEY]
array of time indices
Definition: magnetic.h:66
int16_t int16
Definition: sensor_fusion.h:56
#define MAGBUFFSIZEY
y dimension in magnetometer buffer (12x24 equals 288 elements)
Definition: magnetic.h:45
int8_t int8
Definition: sensor_fusion.h:55
int16_t iBs[3][MAGBUFFSIZEX][MAGBUFFSIZEY]
uncalibrated magnetometer readings
Definition: magnetic.h:65

+ Here is the caller graph for this function: